﻿using System;
using System.Net;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;

namespace DownLoadHttp
{
    public class HttpDownLoad
    {
        private long fileLength;//服务器文件大小
        private long downLength;//已经下载文件大小
        public bool stopDown;//下载暂停终止状态
        public bool isCover { get; set; }
        public string filename { get; set; }
        public bool downOK { get; set; }
        public OnProcess onpr { get; set; }

        public delegate void OnProcess(int prcent, string msg);

        public HttpDownLoad()
        {
            fileLength = 0;
            downLength = 0;
            stopDown = false;
        }

        private string FormatData(UInt64 t)
        {
            double k = (double)t;
            if (k < 1000.0) return k.ToString("f2") + "Byte";
            if ((k > 1000.0) && (k < (1000000.0)))
            {
                k = k / 1000.0;
                return k.ToString("f2") + "Kbyte";
            }
            if ((k > 1000000.0) && (k < 1000000000.0))
            {
                k = k / 1000000.0;
                return k.ToString("f2") + "Mbyte";
            }
            if ((k > 1000000000.0) && (k < 1000000000000.0))
            {
                k = k / 1000000000.0;
                return k.ToString("f2") + "Gbyte";
            }
            else
            {
                return "NULL";
            }
        }
        /// <summary>
        /// 获取下载文件大小
        /// </summary>
        /// <param name="url">连接</param>
        /// <returns>文件长度</returns>
        public long getDownLength(string url)
        {
            try
            {
                WebRequest wrq = WebRequest.Create(url);
                WebResponse wrp = (WebResponse)wrq.GetResponse();
                wrp.Close();
                return wrp.ContentLength;
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return 0;
            }
        }

        /// <summary>
        /// 获取下载文件名称
        /// </summary>
        /// <param name="url">连接</param>
        /// <returns>文件名称</returns>
        public string getFileName(string url)
        {
            string ret = "";
            try
            {
                WebRequest wrq = WebRequest.Create(url);
                WebResponse wrp = (WebResponse)wrq.GetResponse();
                foreach (string key in wrp.Headers.Keys)
                {
                    //ret += key + "\r\n";
                    if (key == "Content-Disposition")//获取文件名
                    {
                        ret = wrp.Headers[key];
                        ret = ret.Substring(ret.IndexOf('"') + 1);
                        ret = ret.Substring(0, ret.Length - 1);
                        break;
                    }
                }
                if ((ret == "") || (ret.IndexOf('.') == -1))
                {
                    ret = url.Substring(url.LastIndexOf("/") + 1);
                }
                wrp.Close();
                return ret;
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return "";
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="url">文件网络地址</param>
        /// <param name="fileName">保存本地文件路径</param>
        /// <param name="progressBar">进度条</param>
        /// <param name="lable">信息标签</param>
        public void httpDownFile(string url, string fileName)
        {
            stopDown = false;
            int provalue = 0;
            long lStartPos = 0;
            string dirname = string.Empty;
            Stream fs = null, ns = null;
            downOK = false;
            fileLength = this.getDownLength(url);//得到网络文件大小

            if (fileLength > 0)
            {
                dirname = fileName.Substring(0, fileName.LastIndexOf("\\"));
                if (!Directory.Exists(dirname))
                {
                    Directory.CreateDirectory(dirname);
                }
                if (isCover && File.Exists(fileName))
                {
                    File.SetAttributes(fileName, FileAttributes.Normal);
                    File.Delete(fileName);                    
                }
                if (File.Exists(fileName))
                {
                    //打开文件，并得到已经下载量
                    fs = File.OpenWrite(fileName);
                    lStartPos = downLength = fs.Length;

                    //进度条
                    provalue = (int)((downLength / fileLength) * 100);

                    if (downLength == fileLength)
                    {
                        if (onpr != null) onpr(provalue, "File has been downloaded!");
                        downOK = true;
                        fs.Close();
                        fs.Dispose();
                        return;
                    }
                    else
                    {
                        //移动文件流中的当前指针
                        fs.Seek(lStartPos, SeekOrigin.Current);
                        if (onpr != null) onpr(provalue, "retransmission...");
                    }
                }
                else //文件不存在则重新创建   
                {
                    downLength = 0;
                    try
                    {
                        fs = new System.IO.FileStream(fileName, FileMode.Create);
                        lStartPos = 0;
                        if (onpr != null) onpr(provalue, "Ready to download...");
                    }
                    catch (IOException e)
                    {
                        if (onpr != null) onpr(provalue, "File create fail. " + e.Message);
                    };
                }

                if (fs == null)
                {
                    return;
                }
                //打开网络连接 
                try
                {
                    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
                    request.Timeout = 5000;
                    if (lStartPos > 0)
                    {
                        request.AddRange((int)lStartPos); //设置Range值 
                    }
                    //向服务器请求，获得服务器回应数据流 
                    ns = request.GetResponse().GetResponseStream();
                    byte[] nbytes = new byte[819200];
                    int nReadSize = 0;
                    nReadSize = ns.Read(nbytes, 0, nbytes.Length);
                    int time1 = Environment.TickCount;
                    int rcount = 0;
                    int recvtry = 0;
                    while (downLength < fileLength)
                    {
                        //是否暂停下载
                        if (stopDown) break;

                        downLength += nReadSize;//已经下载大小  
                        rcount += nReadSize;
                        fs.Write(nbytes, 0, nReadSize);//写文件
                        fs.Flush();//写入磁盘
                        try
                        {
                            nReadSize = ns.Read(nbytes, 0, nbytes.Length); //继续读流
                            if (nReadSize == 0) recvtry++;
                        }
                        catch (InvalidOperationException e)
                        {
                            recvtry++;
                            if (onpr != null) onpr(provalue, e.Message);
                        }
                        if (downLength > 0)
                        {
                            provalue = (int)(((float)downLength / (float)fileLength) * 100.0);
                        }

                        if ((Environment.TickCount - time1) >= 1000)
                        {
                            if (onpr != null) onpr(provalue, "Already Download  " + provalue + "%  speed: " + FormatData((ulong)rcount));
                            time1 = Environment.TickCount;
                            rcount = 0;
                        }
                        if (recvtry > 3) break;
                        Application.DoEvents();
                    }
                    if (downLength == fileLength)
                    {
                        provalue = 100;
                        if (onpr != null) onpr(provalue, "Already Download  " + provalue + "%  speed: " + FormatData((ulong)rcount));
                        downOK = true;
                    }
                    fs.Close();
                    fs.Dispose();
                    ns.Close();
                    ns.Dispose();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    if (fs != null)
                    {
                        fs.Close();
                        fs.Dispose();
                    }

                    if (ns != null)
                    {
                        ns.Close();
                        ns.Dispose();
                    }
                }
            }
        }
    }
}
